#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fstream>
#include "Socket.hpp"

using namespace std;

const string wwwroot = "./wwwroot";//web根目录
const string sep = "\r\n"; //分隔符
const string homepage = "index.html";

class HttpRequst
{
public:
    HttpRequst()
    {}

    //给http请求做反序列化
    void Deserialize(string requst)
    {
        while(true)
        {
            size_t pos = requst.find(sep);
            if(pos == string::npos)
            {
                break;
            }

            string line = requst.substr(0, pos);
            if(line.empty())
            {
                break;
            }
 
            req_header.push_back(line);
            requst.erase(0, pos+sep.size());
        }

        text = requst; 
    }
    
    //把第一行的信息按照空格分隔
    void Parse()
    {
        stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;

        //如果我们的url只是"/", 那么我们就直接跳转到homepage
        file_path = wwwroot;
        if(url == "/" || url == "/index.html") // "/" -> "wwwroot/index.html"
        {
            file_path += "/";
            file_path += homepage; //"wwwroot/index.html"
        }
        else
        {
            file_path += url; // "/a/b/c/d.html" -> "wwwroot/a/b/c/d.html"
        }

        size_t pos = file_path.rfind(".");
        suffix = file_path.substr(pos);
    }

    void DebugPrint()
    {
        for(auto& ch : req_header)
        {
            cout << "--------------------------------------------------" << endl;
            cout << ch << endl;
        }

        cout << text << endl;

        cout << "method: " << method << endl;
        cout << "url: " << url << endl;
        cout << "http_version: " << http_version << endl;
        cout << "file_path: " << file_path << endl;
        cout << "suffix: " << suffix << endl;
    }

public:
    vector<string> req_header;//头部
    string text;//内容

    //我们需要进一步解析req_header后面的结果
    string method;
    string url;
    string http_version;
    string file_path;

    string suffix;
};

class HttpServer;

class ThreadData
{
public:
    ThreadData(HttpServer* s)
    :_newfd(-1), svr(s)
    {}
public:
    int _newfd;
    HttpServer* svr;
};

class HttpServer
{
public:
    HttpServer(uint16_t port)
    :_port(port)
    {
        Content_Type.insert({".html", "text/html"});
        Content_Type.insert({".png", "image/png"});
    }

    ~HttpServer()
    {}

    string SuffixToDesc(string suffix)
    {
        auto iter = Content_Type.find(suffix);
        if(iter == Content_Type.end())
        {
            return Content_Type[".html"];
        }

        return Content_Type[suffix];
    }

    //用cpp的方式去读文件
    static string ReadHtmlContent(const string& htmlpath)
    {   
        //以二进制的方式去读取图片
        ifstream in(htmlpath, ios_base::binary);
        if(!in.is_open()) return "";

        in.seekg(0, ios_base::end);
        auto len = in.tellg();
        in.seekg(0, ios_base::beg);

        string content;
        content.resize(len);

        in.read((char*)content.c_str(),content.size());

        //以文本的方式去读
        // string content;
        // string line;
        // while(getline(in, line))
        // {
        //     content += line;
        // }

        // in.close();

        return content;
    }

    //把客户端发送的请求协议给打印出来, 把这个函数写成非静态的就有this指针, 就可以在里面调用同样的非静态的函数
    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t m = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if(m > 0)
        {
            buffer[m] = 0;
            cout << buffer;//把客户端发送的请求协议给打印出来
            //假设我们读到的就是一个完整的http请求
            HttpRequst req;
            req.Deserialize(buffer);
            req.Parse();
            req.DebugPrint();

            //自己构建返回的协议的格式
            // version 1:直接在源代码中拼, 太挫了我们希望是采用读取文件的方式
            // string text = "<html><body><h3>Xupeipeng is a dashabi</h3></body></html>";

            // version 2: 把需要的内容放在文件里面, 封装一个函数来读取文件的内容
            // version 3: 我们需要根据客户端发送的不同请求来给予客户端不同的响应, 所以需要解析客户端发送的请求
            // version 4: 增加重定向的功能
            bool ok = true;
            string text;
            text = ReadHtmlContent(req.file_path);
            if(text.empty())
            {
                ok = false;
                text = ReadHtmlContent("./wwwroot/err.html");
            }

            string responce_line;
            if(ok)
            {
                responce_line = "Http/1.0 200 OK\r\n";
            }
            else
            {
                responce_line = "Http/1.0 404 NotFound\r\n";
            }

            string responce_header = "Content-length: ";
            responce_header += to_string(text.size());
            responce_header += "\r\n";
            // responce_header += "Location: http://www.qq.com\r\n";
            responce_header += "Set-Cookie: name=hx\r\n";
            responce_header += "Set-Cookie: passwd=znsb1234\r\n";
            responce_header += "Content-Type: ";
            responce_header += SuffixToDesc(req.suffix);
            responce_header += "\r\n";

            string block_line = "\r\n";

            string responce;
            responce = responce_line;
            responce += responce_header;
            responce += block_line;
            responce += text;

            //把自己构建的Responce发送给客户端(浏览器Http协议)
            ssize_t n = send(sockfd, responce.c_str(), responce.size(), 0);
        }
    }

    //服务器在读取客户端发送的数据报
    static void* Routine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        
        td->svr->HandlerHttp(td->_newfd);
        
        delete td;

        return nullptr;
    }

    void Start()
    {
        _listensock.Create();
        _listensock.Bind(_port);
        _listensock.Listen();

        while(true)
        {
            std::string clientip;
            uint16_t clientport;
            ThreadData* td = new ThreadData(this);

            td->_newfd = _listensock.Accpet(&clientip, &clientport);
            if(td->_newfd < 0)
            {
                continue;
            }
            lg(Info, "get a new connect...");

            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);
        }
    }
private:
    Sock _listensock;
    uint16_t _port;
    unordered_map<string, string> Content_Type;
};